﻿using System;

namespace Daystate.ConfigurationManager
{
    public class Parser
    {
        /// <summary>
        /// Generic parser. Accepts only supported types.
        /// </summary>
        /// <typeparam name="T">Returned value type.</typeparam>
        /// <returns>Parsed value.</returns>
        public static T Parse<T>(object value)
        {
            Type valType = typeof(T);

            // Check is the value type supported.
            if (!SupportedTypes.TypesList.ContainsSecondValue(valType))
                throw new ParserException("Unsuported file type.");

            // Try parsing
            try
            {
                object parsedValue = null;
                switch (SupportedTypes.TypesList.GetBySecond(valType))
                {
                    case SupportedTypesEnum.String:
                        parsedValue = Parser.ParseToString(value);
                        break;
                    case SupportedTypesEnum.Integer:
                        parsedValue = Parser.ParseToInt(value);
                        break;
                    case SupportedTypesEnum.Boolean:
                        parsedValue = Parser.ParseToBoolean(value);
                        break;
                    case SupportedTypesEnum.Float:
                        parsedValue = Parser.ParseToFloat(value);
                        break;
                    case SupportedTypesEnum.Double:
                        parsedValue = Parser.ParseToDouble(value);
                        break;
                }

                return (T)parsedValue;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                throw e;
                // return Activator.CreateInstance<V>();                
            }
        }

        /// <summary>
        /// Parses object type value to integer type value.
        /// Casts object to string before parsing.
        /// </summary>
        /// <param name="value">Object type value to be parsed.</param>
        /// <returns>Parsed integer value.</returns>
        public static int ParseToInt(object value)
        {
            if (value is int)
                return (int)value;

            int intValue;
            bool parsingResult = int.TryParse(value as string, out intValue);
            if (parsingResult)
            {
                return intValue;
            }
            else
                throw new ParserException("Parsing has failed! Cannot parse given object to integer type.");
        }


        /// <summary>
        /// Parses object type value to string type value.
        /// Casts object to string before parsing.
        /// </summary>
        /// <param name="value">Object type value to be parsed.</param>
        /// <returns>Parsed integer value.</returns>
        public static string ParseToString(object value)
        {
            string stringValue;
            bool parsingResult = !string.IsNullOrWhiteSpace(value as string);

            if (parsingResult)
            {
                stringValue = value as string;
            }
            else
            {
                stringValue = string.Empty;
            }

            return stringValue;
        }

        /// <summary>
        /// Parses object type value to boolean type value.
        /// Casts object to string before parsing.
        /// </summary>
        /// <param name="value">Object type value to be parsed.</param>
        /// <returns>Parsed boolean value.</returns>
        public static bool ParseToBoolean(object value)
        {
            if (value is bool)
                return (bool)value;

            bool booleanValue;
            bool parsingResult = bool.TryParse(value as string, out booleanValue);
            if (parsingResult)
            {
                return booleanValue;
            }
            else
                throw new ParserException(string.Format("Parsing has failed! Cannot parse given ({0}) object to boolean type.", value.GetType()));
        }

        /// <summary>
        /// Parses object type value to double type value.
        /// Casts object to string before parsing.
        /// </summary>
        /// <param name="value">Object type value to be parsed.</param>
        /// <returns>Parsed double value.</returns>
        public static double ParseToDouble(object value)
        {
            if (value is double)
                return (double)value;

            double doubleValue;
            bool parsingResult = double.TryParse(value as string, out doubleValue);
            if (parsingResult)
            {
                return doubleValue;
            }
            else
                throw new ParserException(string.Format("Parsing has failed! Cannot parse given ({0}) object to double type.", value.GetType()));
        }

        /// <summary>
        /// Parses object type value to double type value.
        /// Casts object to string before parsing.
        /// </summary>
        /// <param name="value">Object type value to be parsed.</param>
        /// <returns>Parsed double value.</returns>
        public static double ParseToFloat(object value)
        {
            if (value is float)
                return (float)value;

            float floatValue;
            bool parsingResult = float.TryParse(value as string, out floatValue);
            if (parsingResult)
            {
                return floatValue;
            }
            else
                throw new ParserException(string.Format("Parsing has failed! Cannot parse given ({0}) object to float type.", value.GetType()));
        }
    }


    /// <summary>
    /// Parser common exception.
    /// </summary>
    public class ParserException : Exception
    {
        public ParserException(string msg) : base(msg) { }
    }
}
